/***************************************************************************/
/*                                                                         */
/*  t1decode.c                                                             */
/*                                                                         */
/*    PostScript Type 1 decoding routines (body).                          */
/*                                                                         */
/*  Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009    */
/*            2010 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_INTERNAL_CALC_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_POSTSCRIPT_HINTS_H
#include FT_OUTLINE_H

#include "t1decode.h"
#include "psobjs.h"

#include "psauxerr.h"


/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
#undef  FT_COMPONENT
#define FT_COMPONENT trace_t1decode


typedef enum  T1_Operator_
{
    op_none = 0,
    op_endchar,
    op_hsbw,
    op_seac,
    op_sbw,
    op_closepath,
    op_hlineto,
    op_hmoveto,
    op_hvcurveto,
    op_rlineto,
    op_rmoveto,
    op_rrcurveto,
    op_vhcurveto,
    op_vlineto,
    op_vmoveto,
    op_dotsection,
    op_hstem,
    op_hstem3,
    op_vstem,
    op_vstem3,
    op_div,
    op_callothersubr,
    op_callsubr,
    op_pop,
    op_return,
    op_setcurrentpoint,
    op_unknown15,

    op_max    /* never remove this one */

} T1_Operator;


static
const FT_Int t1_args_count[op_max] =
{
    0, /* none */
    0, /* endchar */
    2, /* hsbw */
    5, /* seac */
    4, /* sbw */
    0, /* closepath */
    1, /* hlineto */
    1, /* hmoveto */
    4, /* hvcurveto */
    2, /* rlineto */
    2, /* rmoveto */
    6, /* rrcurveto */
    4, /* vhcurveto */
    1, /* vlineto */
    1, /* vmoveto */
    0, /* dotsection */
    2, /* hstem */
    6, /* hstem3 */
    2, /* vstem */
    6, /* vstem3 */
    2, /* div */
    -1, /* callothersubr */
    1, /* callsubr */
    0, /* pop */
    0, /* return */
    2, /* setcurrentpoint */
    2  /* opcode 15 (undocumented and obsolete) */
};


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    t1_lookup_glyph_by_stdcharcode                                     */
/*                                                                       */
/* <Description>                                                         */
/*    Looks up a given glyph by its StandardEncoding charcode.  Used to  */
/*    implement the SEAC Type 1 operator.                                */
/*                                                                       */
/* <Input>                                                               */
/*    face     :: The current face object.                               */
/*                                                                       */
/*    charcode :: The character code to look for.                        */
/*                                                                       */
/* <Return>                                                              */
/*    A glyph index in the font face.  Returns -1 if the corresponding   */
/*    glyph wasn't found.                                                */
/*                                                                       */
static FT_Int
t1_lookup_glyph_by_stdcharcode(T1_Decoder decoder,
                               FT_Int charcode)
{
    FT_UInt n;
    const FT_String* glyph_name;
    FT_Service_PsCMaps psnames = decoder->psnames;


    /* check range of standard char code */
    if (charcode < 0 || charcode > 255)
        return -1;

    glyph_name = psnames->adobe_std_strings(
        psnames->adobe_std_encoding[charcode]);

    for (n = 0; n < decoder->num_glyphs; n++)
    {
        FT_String* name = (FT_String*)decoder->glyph_names[n];


        if (name &&
            name[0] == glyph_name[0] &&
            ft_strcmp(name, glyph_name) == 0)
            return n;
    }

    return -1;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    t1operator_seac                                                    */
/*                                                                       */
/* <Description>                                                         */
/*    Implements the `seac' Type 1 operator for a Type 1 decoder.        */
/*                                                                       */
/* <Input>                                                               */
/*    decoder :: The current CID decoder.                                */
/*                                                                       */
/*    asb     :: The accent's side bearing.                              */
/*                                                                       */
/*    adx     :: The horizontal offset of the accent.                    */
/*                                                                       */
/*    ady     :: The vertical offset of the accent.                      */
/*                                                                       */
/*    bchar   :: The base character's StandardEncoding charcode.         */
/*                                                                       */
/*    achar   :: The accent character's StandardEncoding charcode.       */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
static FT_Error
t1operator_seac(T1_Decoder decoder,
                FT_Pos asb,
                FT_Pos adx,
                FT_Pos ady,
                FT_Int bchar,
                FT_Int achar)
{
    FT_Error error;
    FT_Int bchar_index, achar_index;
    #if 0
    FT_Int n_base_points;
    FT_Outline* base = decoder->builder.base;
    #endif
    FT_Vector left_bearing, advance;

    #ifdef FT_CONFIG_OPTION_INCREMENTAL
    T1_Face face = (T1_Face)decoder->builder.face;
    #endif


    if (decoder->seac)
    {
        FT_ERROR(("t1operator_seac: invalid nested seac\n"));
        return PSaux_Err_Syntax_Error;
    }

    /* seac weirdness */
    adx += decoder->builder.left_bearing.x;

    /* `glyph_names' is set to 0 for CID fonts which do not */
    /* include an encoding.  How can we deal with these?    */
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
    if (decoder->glyph_names == 0 &&
        !face->root.internal->incremental_interface)
    #else
    if (decoder->glyph_names == 0)
    #endif /* FT_CONFIG_OPTION_INCREMENTAL */
    {
        FT_ERROR(("t1operator_seac:"
                  " glyph names table not available in this font\n"));
        return PSaux_Err_Syntax_Error;
    }

    #ifdef FT_CONFIG_OPTION_INCREMENTAL
    if (face->root.internal->incremental_interface)
    {
        /* the caller must handle the font encoding also */
        bchar_index = bchar;
        achar_index = achar;
    }
    else
    #endif
    {
        bchar_index = t1_lookup_glyph_by_stdcharcode(decoder, bchar);
        achar_index = t1_lookup_glyph_by_stdcharcode(decoder, achar);
    }

    if (bchar_index < 0 || achar_index < 0)
    {
        FT_ERROR(("t1operator_seac:"
                  " invalid seac character code arguments\n"));
        return PSaux_Err_Syntax_Error;
    }

    /* if we are trying to load a composite glyph, do not load the */
    /* accent character and return the array of subglyphs.         */
    if (decoder->builder.no_recurse)
    {
        FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph;
        FT_GlyphLoader loader = glyph->internal->loader;
        FT_SubGlyph subg;


        /* reallocate subglyph array if necessary */
        error = FT_GlyphLoader_CheckSubGlyphs(loader, 2);
        if (error)
            goto Exit;

        subg = loader->current.subglyphs;

        /* subglyph 0 = base character */
        subg->index = bchar_index;
        subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
                      FT_SUBGLYPH_FLAG_USE_MY_METRICS;
        subg->arg1 = 0;
        subg->arg2 = 0;
        subg++;

        /* subglyph 1 = accent character */
        subg->index = achar_index;
        subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
        subg->arg1 = (FT_Int)FIXED_TO_INT(adx - asb);
        subg->arg2 = (FT_Int)FIXED_TO_INT(ady);

        /* set up remaining glyph fields */
        glyph->num_subglyphs = 2;
        glyph->subglyphs = loader->base.subglyphs;
        glyph->format = FT_GLYPH_FORMAT_COMPOSITE;

        loader->current.num_subglyphs = 2;
        goto Exit;
    }

    /* First load `bchar' in builder */
    /* now load the unscaled outline */

    FT_GlyphLoader_Prepare(decoder->builder.loader);    /* prepare loader */

    /* the seac operator must not be nested */
    decoder->seac = TRUE;
    error = t1_decoder_parse_glyph(decoder, bchar_index);
    decoder->seac = FALSE;
    if (error)
        goto Exit;

    /* save the left bearing and width of the base character */
    /* as they will be erased by the next load.              */

    left_bearing = decoder->builder.left_bearing;
    advance = decoder->builder.advance;

    decoder->builder.left_bearing.x = 0;
    decoder->builder.left_bearing.y = 0;

    decoder->builder.pos_x = adx - asb;
    decoder->builder.pos_y = ady;

    /* Now load `achar' on top of */
    /* the base outline           */

    /* the seac operator must not be nested */
    decoder->seac = TRUE;
    error = t1_decoder_parse_glyph(decoder, achar_index);
    decoder->seac = FALSE;
    if (error)
        goto Exit;

    /* restore the left side bearing and   */
    /* advance width of the base character */

    decoder->builder.left_bearing = left_bearing;
    decoder->builder.advance = advance;

    decoder->builder.pos_x = 0;
    decoder->builder.pos_y = 0;

Exit:
    return error;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    t1_decoder_parse_charstrings                                       */
/*                                                                       */
/* <Description>                                                         */
/*    Parses a given Type 1 charstrings program.                         */
/*                                                                       */
/* <Input>                                                               */
/*    decoder         :: The current Type 1 decoder.                     */
/*                                                                       */
/*    charstring_base :: The base address of the charstring stream.      */
/*                                                                       */
/*    charstring_len  :: The length in bytes of the charstring stream.   */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
t1_decoder_parse_charstrings(T1_Decoder decoder,
                             FT_Byte * charstring_base,
                             FT_UInt charstring_len)
{
    FT_Error error;
    T1_Decoder_Zone zone;
    FT_Byte* ip;
    FT_Byte* limit;
    T1_Builder builder = &decoder->builder;
    FT_Pos x, y, orig_x, orig_y;
    FT_Int known_othersubr_result_cnt = 0;
    FT_Int unknown_othersubr_result_cnt = 0;
    FT_Bool large_int;
    FT_Fixed seed;

    T1_Hints_Funcs hinter;

    #ifdef FT_DEBUG_LEVEL_TRACE
    FT_Bool bol = TRUE;
    #endif


    /* we don't want to touch the source code -- use macro trick */
#define start_point t1_builder_start_point
#define check_points t1_builder_check_points
#define add_point t1_builder_add_point
#define add_point1 t1_builder_add_point1
#define add_contour t1_builder_add_contour
#define close_contour t1_builder_close_contour


    /* compute random seed from stack address of parameter */
    seed = (FT_Fixed)(((FT_PtrDist)(char*)&seed ^
                       (FT_PtrDist)(char*)&decoder ^
                       (FT_PtrDist)(char*)&charstring_base) &
                      FT_ULONG_MAX);
    seed = (seed ^ (seed >> 10) ^ (seed >> 20)) & 0xFFFFL;
    if (seed == 0)
        seed = 0x7384;

    /* First of all, initialize the decoder */
    decoder->top = decoder->stack;
    decoder->zone = decoder->zones;
    zone = decoder->zones;

    builder->parse_state = T1_Parse_Start;

    hinter = (T1_Hints_Funcs)builder->hints_funcs;

    /* a font that reads BuildCharArray without setting */
    /* its values first is buggy, but ...               */
    FT_ASSERT((decoder->len_buildchar == 0) ==
              (decoder->buildchar == NULL));

    if (decoder->len_buildchar > 0)
        ft_memset(&decoder->buildchar[0],
                  0,
                  sizeof(decoder->buildchar[0]) * decoder->len_buildchar);

    FT_TRACE4(("\n"
               "Start charstring\n"));

    zone->base = charstring_base;
    limit = zone->limit = charstring_base + charstring_len;
    ip = zone->cursor = zone->base;

    error = PSaux_Err_Ok;

    x = orig_x = builder->pos_x;
    y = orig_y = builder->pos_y;

    /* begin hints recording session, if any */
    if (hinter)
        hinter->open(hinter->hints);

    large_int = FALSE;

    /* now, execute loop */
    while (ip < limit)
    {
        FT_Long* top = decoder->top;
        T1_Operator op = op_none;
        FT_Int32 value = 0;


        FT_ASSERT(known_othersubr_result_cnt == 0 ||
                  unknown_othersubr_result_cnt == 0);

        #ifdef FT_DEBUG_LEVEL_TRACE
        if (bol)
        {
            FT_TRACE5((" (%d)", decoder->top - decoder->stack));
            bol = FALSE;
        }
        #endif

        /*********************************************************************/
        /*                                                                   */
        /* Decode operator or operand                                        */
        /*                                                                   */
        /*                                                                   */

        /* first of all, decompress operator or value */
        switch (*ip++)
        {
            case 1:
                op = op_hstem;
                break;

            case 3:
                op = op_vstem;
                break;
            case 4:
                op = op_vmoveto;
                break;
            case 5:
                op = op_rlineto;
                break;
            case 6:
                op = op_hlineto;
                break;
            case 7:
                op = op_vlineto;
                break;
            case 8:
                op = op_rrcurveto;
                break;
            case 9:
                op = op_closepath;
                break;
            case 10:
                op = op_callsubr;
                break;
            case 11:
                op = op_return;
                break;

            case 13:
                op = op_hsbw;
                break;
            case 14:
                op = op_endchar;
                break;

            case 15:    /* undocumented, obsolete operator */
                op = op_unknown15;
                break;

            case 21:
                op = op_rmoveto;
                break;
            case 22:
                op = op_hmoveto;
                break;

            case 30:
                op = op_vhcurveto;
                break;
            case 31:
                op = op_hvcurveto;
                break;

            case 12:
                if (ip > limit)
                {
                    FT_ERROR(("t1_decoder_parse_charstrings:"
                              " invalid escape (12+EOF)\n"));
                    goto Syntax_Error;
                }

                switch (*ip++)
                {
                    case 0:
                        op = op_dotsection;
                        break;
                    case 1:
                        op = op_vstem3;
                        break;
                    case 2:
                        op = op_hstem3;
                        break;
                    case 6:
                        op = op_seac;
                        break;
                    case 7:
                        op = op_sbw;
                        break;
                    case 12:
                        op = op_div;
                        break;
                    case 16:
                        op = op_callothersubr;
                        break;
                    case 17:
                        op = op_pop;
                        break;
                    case 33:
                        op = op_setcurrentpoint;
                        break;

                    default:
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " invalid escape (12+%d)\n",
                                  ip[-1]));
                        goto Syntax_Error;
                }
                break;

            case 255: /* four bytes integer */
                if (ip + 4 > limit)
                {
                    FT_ERROR(("t1_decoder_parse_charstrings:"
                              " unexpected EOF in integer\n"));
                    goto Syntax_Error;
                }

                value = (FT_Int32)(((FT_Long)ip[0] << 24) |
                                   ((FT_Long)ip[1] << 16) |
                                   ((FT_Long)ip[2] << 8) |
                                   ip[3]);
                ip += 4;

                /* According to the specification, values > 32000 or < -32000 must */
                /* be followed by a `div' operator to make the result be in the    */
                /* range [-32000;32000].  We expect that the second argument of    */
                /* `div' is not a large number.  Additionally, we don't handle     */
                /* stuff like `<large1> <large2> <num> div <num> div' or           */
                /* <large1> <large2> <num> div div'.  This is probably not allowed */
                /* anyway.                                                         */
                if (value > 32000 || value < -32000)
                {
                    if (large_int)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " no `div' after large integer\n"));
                    }
                    else
                        large_int = TRUE;
                }
                else
                {
                    if (!large_int)
                        value <<= 16;
                }

                break;

            default:
                if (ip[-1] >= 32)
                {
                    if (ip[-1] < 247)
                        value = (FT_Int32)ip[-1] - 139;
                    else
                    {
                        if (++ip > limit)
                        {
                            FT_ERROR(("t1_decoder_parse_charstrings:"
                                      " unexpected EOF in integer\n"));
                            goto Syntax_Error;
                        }

                        if (ip[-2] < 251)
                            value = (((FT_Int32)ip[-2] - 247) << 8) + ip[-1] + 108;
                        else
                            value = -((((FT_Int32)ip[-2] - 251) << 8) + ip[-1] + 108);
                    }

                    if (!large_int)
                        value <<= 16;
                }
                else
                {
                    FT_ERROR(("t1_decoder_parse_charstrings:"
                              " invalid byte (%d)\n", ip[-1]));
                    goto Syntax_Error;
                }
        }

        if (unknown_othersubr_result_cnt > 0)
        {
            switch (op)
            {
                case op_callsubr:
                case op_return:
                case op_none:
                case op_pop:
                    break;

                default:
                    /* all operands have been transferred by previous pops */
                    unknown_othersubr_result_cnt = 0;
                    break;
            }
        }

        if (large_int && !(op == op_none || op == op_div))
        {
            FT_ERROR(("t1_decoder_parse_charstrings:"
                      " no `div' after large integer\n"));

            large_int = FALSE;
        }

        /*********************************************************************/
        /*                                                                   */
        /*  Push value on stack, or process operator                         */
        /*                                                                   */
        /*                                                                   */
        if (op == op_none)
        {
            if (top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS)
            {
                FT_ERROR(("t1_decoder_parse_charstrings: stack overflow\n"));
                goto Syntax_Error;
            }

            #ifdef FT_DEBUG_LEVEL_TRACE
            if (large_int)
                FT_TRACE4((" %ld", value));
            else
                FT_TRACE4((" %ld", (FT_Int32)(value >> 16)));
            #endif

            *top++ = value;
            decoder->top = top;
        }
        else if (op == op_callothersubr)  /* callothersubr */
        {
            FT_Int subr_no;
            FT_Int arg_cnt;


            #ifdef FT_DEBUG_LEVEL_TRACE
            FT_TRACE4((" callothersubr\n"));
            bol = TRUE;
            #endif

            if (top - decoder->stack < 2)
                goto Stack_Underflow;

            top -= 2;

            subr_no = (FT_Int)(top[1] >> 16);
            arg_cnt = (FT_Int)(top[0] >> 16);

            /***********************************************************/
            /*                                                         */
            /* remove all operands to callothersubr from the stack     */
            /*                                                         */
            /* for handled othersubrs, where we know the number of     */
            /* arguments, we increase the stack by the value of        */
            /* known_othersubr_result_cnt                              */
            /*                                                         */
            /* for unhandled othersubrs the following pops adjust the  */
            /* stack pointer as necessary                              */

            if (arg_cnt > top - decoder->stack)
                goto Stack_Underflow;

            top -= arg_cnt;

            known_othersubr_result_cnt = 0;
            unknown_othersubr_result_cnt = 0;

            /* XXX TODO: The checks to `arg_count == <whatever>'       */
            /* might not be correct; an othersubr expects a certain    */
            /* number of operands on the PostScript stack (as opposed  */
            /* to the T1 stack) but it doesn't have to put them there  */
            /* by itself; previous othersubrs might have left the      */
            /* operands there if they were not followed by an          */
            /* appropriate number of pops                              */
            /*                                                         */
            /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */
            /* accept a font that contains charstrings like            */
            /*                                                         */
            /*     100 200 2 20 callothersubr                          */
            /*     300 1 20 callothersubr pop                          */
            /*                                                         */
            /* Perhaps this is the reason why BuildCharArray exists.   */

            switch (subr_no)
            {
                case 1:             /* start flex feature */
                    if (arg_cnt != 0)
                        goto Unexpected_OtherSubr;

                    decoder->flex_state = 1;
                    decoder->num_flex_vectors = 0;
                    if (start_point(builder, x, y) ||
                        check_points(builder, 6))
                        goto Fail;
                    break;

                case 2:             /* add flex vectors */
                {
                    FT_Int idx;


                    if (arg_cnt != 0)
                        goto Unexpected_OtherSubr;

                    /* note that we should not add a point for index 0; */
                    /* this will move our current position to the flex  */
                    /* point without adding any point to the outline    */
                    idx = decoder->num_flex_vectors++;
                    if (idx > 0 && idx < 7)
                        add_point(builder,
                                  x,
                                  y,
                                  (FT_Byte)(idx == 3 || idx == 6));
                }
                break;

                case 0:             /* end flex feature */
                    if (arg_cnt != 3)
                        goto Unexpected_OtherSubr;

                    if (decoder->flex_state == 0 ||
                        decoder->num_flex_vectors != 7)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " unexpected flex end\n"));
                        goto Syntax_Error;
                    }

                    /* the two `results' are popped by the following setcurrentpoint */
                    known_othersubr_result_cnt = 2;
                    break;

                case 3:             /* change hints */
                    if (arg_cnt != 1)
                        goto Unexpected_OtherSubr;

                    known_othersubr_result_cnt = 1;

                    if (hinter)
                        hinter->reset(hinter->hints, builder->current->n_points);
                    break;

                case 12:
                case 13:
                    /* counter control hints, clear stack */
                    top = decoder->stack;
                    break;

                case 14:
                case 15:
                case 16:
                case 17:
                case 18:            /* multiple masters */
                {
                    PS_Blend blend = decoder->blend;
                    FT_UInt num_points, nn, mm;
                    FT_Long* delta;
                    FT_Long* values;


                    if (!blend)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " unexpected multiple masters operator\n"));
                        goto Syntax_Error;
                    }

                    num_points = (FT_UInt)subr_no - 13 + (subr_no == 18);
                    if (arg_cnt != (FT_Int)(num_points * blend->num_designs))
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " incorrect number of multiple masters arguments\n"));
                        goto Syntax_Error;
                    }

                    /* we want to compute:                                   */
                    /*                                                       */
                    /*  a0*w0 + a1*w1 + ... + ak*wk                          */
                    /*                                                       */
                    /* but we only have the a0, a1-a0, a2-a0, .. ak-a0       */
                    /* however, given that w0 + w1 + ... + wk == 1, we can   */
                    /* rewrite it easily as:                                 */
                    /*                                                       */
                    /*  a0 + (a1-a0)*w1 + (a2-a0)*w2 + .. + (ak-a0)*wk       */
                    /*                                                       */
                    /* where k == num_designs-1                              */
                    /*                                                       */
                    /* I guess that's why it's written in this `compact'     */
                    /* form.                                                 */
                    /*                                                       */
                    delta = top + num_points;
                    values = top;
                    for (nn = 0; nn < num_points; nn++)
                    {
                        FT_Long tmp = values[0];


                        for (mm = 1; mm < blend->num_designs; mm++)
                            tmp += FT_MulFix(*delta++, blend->weight_vector[mm]);

                        *values++ = tmp;
                    }

                    known_othersubr_result_cnt = num_points;
                    break;
                }

                case 19:
                    /* <idx> 1 19 callothersubr                             */
                    /* => replace elements starting from index cvi( <idx> ) */
                    /*    of BuildCharArray with WeightVector               */
                {
                    FT_Int idx;
                    PS_Blend blend = decoder->blend;


                    if (arg_cnt != 1 || blend == NULL)
                        goto Unexpected_OtherSubr;

                    idx = (FT_Int)(top[0] >> 16);

                    if (idx < 0 ||
                        idx + blend->num_designs > decoder->len_buildchar)
                        goto Unexpected_OtherSubr;

                    ft_memcpy(&decoder->buildchar[idx],
                              blend->weight_vector,
                              blend->num_designs *
                              sizeof(blend->weight_vector[0]));
                }
                break;

                case 20:
                    /* <arg1> <arg2> 2 20 callothersubr pop   */
                    /* ==> push <arg1> + <arg2> onto T1 stack */
                    if (arg_cnt != 2)
                        goto Unexpected_OtherSubr;

                    top[0] += top[1]; /* XXX (over|under)flow */

                    known_othersubr_result_cnt = 1;
                    break;

                case 21:
                    /* <arg1> <arg2> 2 21 callothersubr pop   */
                    /* ==> push <arg1> - <arg2> onto T1 stack */
                    if (arg_cnt != 2)
                        goto Unexpected_OtherSubr;

                    top[0] -= top[1]; /* XXX (over|under)flow */

                    known_othersubr_result_cnt = 1;
                    break;

                case 22:
                    /* <arg1> <arg2> 2 22 callothersubr pop   */
                    /* ==> push <arg1> * <arg2> onto T1 stack */
                    if (arg_cnt != 2)
                        goto Unexpected_OtherSubr;

                    top[0] = FT_MulFix(top[0], top[1]);

                    known_othersubr_result_cnt = 1;
                    break;

                case 23:
                    /* <arg1> <arg2> 2 23 callothersubr pop   */
                    /* ==> push <arg1> / <arg2> onto T1 stack */
                    if (arg_cnt != 2 || top[1] == 0)
                        goto Unexpected_OtherSubr;

                    top[0] = FT_DivFix(top[0], top[1]);

                    known_othersubr_result_cnt = 1;
                    break;

                case 24:
                    /* <val> <idx> 2 24 callothersubr               */
                    /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
                {
                    FT_Int idx;
                    PS_Blend blend = decoder->blend;


                    if (arg_cnt != 2 || blend == NULL)
                        goto Unexpected_OtherSubr;

                    idx = (FT_Int)(top[1] >> 16);

                    if (idx < 0 || (FT_UInt)idx >= decoder->len_buildchar)
                        goto Unexpected_OtherSubr;

                    decoder->buildchar[idx] = top[0];
                }
                break;

                case 25:
                    /* <idx> 1 25 callothersubr pop        */
                    /* ==> push BuildCharArray[cvi( idx )] */
                    /*     onto T1 stack                   */
                {
                    FT_Int idx;
                    PS_Blend blend = decoder->blend;


                    if (arg_cnt != 1 || blend == NULL)
                        goto Unexpected_OtherSubr;

                    idx = (FT_Int)(top[0] >> 16);

                    if (idx < 0 || (FT_UInt)idx >= decoder->len_buildchar)
                        goto Unexpected_OtherSubr;

                    top[0] = decoder->buildchar[idx];
                }

                    known_othersubr_result_cnt = 1;
                    break;

                    #if 0
                case 26:
                    /* <val> mark <idx> ==> set BuildCharArray[cvi( <idx> )] = <val>, */
                    /*                      leave mark on T1 stack                    */
                    /* <val> <idx>      ==> set BuildCharArray[cvi( <idx> )] = <val>  */
                    XXX which routine has left its mark on the(PostScript) stack ?;
                    break;
                    #endif

                case 27 :
                    /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */
                    /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */
                    /*     otherwise push <res2>                          */
                    if (arg_cnt != 4)
                        goto Unexpected_OtherSubr;

                    if (top[2] > top[3])
                        top[0] = top[1];

                    known_othersubr_result_cnt = 1;
                    break;

                case 28:
                    /* 0 28 callothersubr pop                               */
                    /* => push random value from interval [0, 1) onto stack */
                    if (arg_cnt != 0)
                        goto Unexpected_OtherSubr;

                    {
                        FT_Fixed Rand;


                        Rand = seed;
                        if (Rand >= 0x8000L)
                            Rand++;

                        top[0] = Rand;

                        seed = FT_MulFix(seed, 0x10000L - seed);
                        if (seed == 0)
                            seed += 0x2873;
                    }

                    known_othersubr_result_cnt = 1;
                    break;

                default:
                    FT_ERROR(("t1_decoder_parse_charstrings:"
                              " unknown othersubr [%d %d], wish me luck\n",
                              arg_cnt, subr_no));
                    unknown_othersubr_result_cnt = arg_cnt;
                    break;

Unexpected_OtherSubr:
                    FT_ERROR(("t1_decoder_parse_charstrings:"
                              " invalid othersubr [%d %d]\n", arg_cnt, subr_no));
                    goto Syntax_Error;
            }

            top += known_othersubr_result_cnt;

            decoder->top = top;
        }
        else /* general operator */
        {
            FT_Int num_args = t1_args_count[op];


            FT_ASSERT(num_args >= 0);

            if (top - decoder->stack < num_args)
                goto Stack_Underflow;

            /* XXX Operators usually take their operands from the        */
            /*     bottom of the stack, i.e., the operands are           */
            /*     decoder->stack[0], ..., decoder->stack[num_args - 1]; */
            /*     only div, callsubr, and callothersubr are different.  */
            /*     In practice it doesn't matter (?).                    */

            #ifdef FT_DEBUG_LEVEL_TRACE

            switch (op)
            {
                case op_callsubr:
                case op_div:
                case op_callothersubr:
                case op_pop:
                case op_return:
                    break;

                default:
                    if (top - decoder->stack != num_args)
                        FT_TRACE0(("t1_decoder_parse_charstrings:"
                                   " too much operands on the stack"
                                   " (seen %d, expected %d)\n",
                                   top - decoder->stack, num_args));
                    break;
            }

            #endif /* FT_DEBUG_LEVEL_TRACE */

            top -= num_args;

            switch (op)
            {
                case op_endchar:
                    FT_TRACE4((" endchar\n"));

                    close_contour(builder);

                    /* close hints recording session */
                    if (hinter)
                    {
                        if (hinter->close(hinter->hints, builder->current->n_points))
                            goto Syntax_Error;

                        /* apply hints to the loaded glyph outline now */
                        hinter->apply(hinter->hints,
                                      builder->current,
                                      (PSH_Globals)builder->hints_globals,
                                      decoder->hint_mode);
                    }

                    /* add current outline to the glyph slot */
                    FT_GlyphLoader_Add(builder->loader);

                    /* the compiler should optimize away this empty loop but ... */

                    #ifdef FT_DEBUG_LEVEL_TRACE

                    if (decoder->len_buildchar > 0)
                    {
                        FT_UInt i;


                        FT_TRACE4(("BuildCharArray = [ "));

                        for (i = 0; i < decoder->len_buildchar; ++i)
                            FT_TRACE4(("%d ", decoder->buildchar[i]));

                        FT_TRACE4(("]\n"));
                    }

                    #endif /* FT_DEBUG_LEVEL_TRACE */

                    FT_TRACE4(("\n"));

                    /* return now! */
                    return PSaux_Err_Ok;

                case op_hsbw:
                    FT_TRACE4((" hsbw"));

                    builder->parse_state = T1_Parse_Have_Width;

                    builder->left_bearing.x += top[0];
                    builder->advance.x = top[1];
                    builder->advance.y = 0;

                    orig_x = x = builder->pos_x + top[0];
                    orig_y = y = builder->pos_y;

                    FT_UNUSED(orig_y);

                    /* the `metrics_only' indicates that we only want to compute */
                    /* the glyph's metrics (lsb + advance width), not load the   */
                    /* rest of it; so exit immediately                           */
                    if (builder->metrics_only)
                        return PSaux_Err_Ok;

                    break;

                case op_seac:
                    return t1operator_seac(decoder,
                                           top[0],
                                           top[1],
                                           top[2],
                                           (FT_Int)(top[3] >> 16),
                                           (FT_Int)(top[4] >> 16));

                case op_sbw:
                    FT_TRACE4((" sbw"));

                    builder->parse_state = T1_Parse_Have_Width;

                    builder->left_bearing.x += top[0];
                    builder->left_bearing.y += top[1];
                    builder->advance.x = top[2];
                    builder->advance.y = top[3];

                    x = builder->pos_x + top[0];
                    y = builder->pos_y + top[1];

                    /* the `metrics_only' indicates that we only want to compute */
                    /* the glyph's metrics (lsb + advance width), not load the   */
                    /* rest of it; so exit immediately                           */
                    if (builder->metrics_only)
                        return PSaux_Err_Ok;

                    break;

                case op_closepath:
                    FT_TRACE4((" closepath"));

                    /* if there is no path, `closepath' is a no-op */
                    if (builder->parse_state == T1_Parse_Have_Path ||
                        builder->parse_state == T1_Parse_Have_Moveto)
                        close_contour(builder);

                    builder->parse_state = T1_Parse_Have_Width;
                    break;

                case op_hlineto:
                    FT_TRACE4((" hlineto"));

                    if (start_point(builder, x, y))
                        goto Fail;

                    x += top[0];
                    goto Add_Line;

                case op_hmoveto:
                    FT_TRACE4((" hmoveto"));

                    x += top[0];
                    if (!decoder->flex_state)
                    {
                        if (builder->parse_state == T1_Parse_Start)
                            goto Syntax_Error;
                        builder->parse_state = T1_Parse_Have_Moveto;
                    }
                    break;

                case op_hvcurveto:
                    FT_TRACE4((" hvcurveto"));

                    if (start_point(builder, x, y) ||
                        check_points(builder, 3))
                        goto Fail;

                    x += top[0];
                    add_point(builder, x, y, 0);
                    x += top[1];
                    y += top[2];
                    add_point(builder, x, y, 0);
                    y += top[3];
                    add_point(builder, x, y, 1);
                    break;

                case op_rlineto:
                    FT_TRACE4((" rlineto"));

                    if (start_point(builder, x, y))
                        goto Fail;

                    x += top[0];
                    y += top[1];

Add_Line:
                    if (add_point1(builder, x, y))
                        goto Fail;
                    break;

                case op_rmoveto:
                    FT_TRACE4((" rmoveto"));

                    x += top[0];
                    y += top[1];
                    if (!decoder->flex_state)
                    {
                        if (builder->parse_state == T1_Parse_Start)
                            goto Syntax_Error;
                        builder->parse_state = T1_Parse_Have_Moveto;
                    }
                    break;

                case op_rrcurveto:
                    FT_TRACE4((" rrcurveto"));

                    if (start_point(builder, x, y) ||
                        check_points(builder, 3))
                        goto Fail;

                    x += top[0];
                    y += top[1];
                    add_point(builder, x, y, 0);

                    x += top[2];
                    y += top[3];
                    add_point(builder, x, y, 0);

                    x += top[4];
                    y += top[5];
                    add_point(builder, x, y, 1);
                    break;

                case op_vhcurveto:
                    FT_TRACE4((" vhcurveto"));

                    if (start_point(builder, x, y) ||
                        check_points(builder, 3))
                        goto Fail;

                    y += top[0];
                    add_point(builder, x, y, 0);
                    x += top[1];
                    y += top[2];
                    add_point(builder, x, y, 0);
                    x += top[3];
                    add_point(builder, x, y, 1);
                    break;

                case op_vlineto:
                    FT_TRACE4((" vlineto"));

                    if (start_point(builder, x, y))
                        goto Fail;

                    y += top[0];
                    goto Add_Line;

                case op_vmoveto:
                    FT_TRACE4((" vmoveto"));

                    y += top[0];
                    if (!decoder->flex_state)
                    {
                        if (builder->parse_state == T1_Parse_Start)
                            goto Syntax_Error;
                        builder->parse_state = T1_Parse_Have_Moveto;
                    }
                    break;

                case op_div:
                    FT_TRACE4((" div"));

                    /* if `large_int' is set, we divide unscaled numbers; */
                    /* otherwise, we divide numbers in 16.16 format --    */
                    /* in both cases, it is the same operation            */
                    *top = FT_DivFix(top[0], top[1]);
                    ++top;

                    large_int = FALSE;
                    break;

                case op_callsubr:
                {
                    FT_Int idx;


                    FT_TRACE4((" callsubr"));

                    idx = (FT_Int)(top[0] >> 16);
                    if (idx < 0 || idx >= (FT_Int)decoder->num_subrs)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " invalid subrs index\n"));
                        goto Syntax_Error;
                    }

                    if (zone - decoder->zones >= T1_MAX_SUBRS_CALLS)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " too many nested subrs\n"));
                        goto Syntax_Error;
                    }

                    zone->cursor = ip; /* save current instruction pointer */

                    zone++;

                    /* The Type 1 driver stores subroutines without the seed bytes. */
                    /* The CID driver stores subroutines with seed bytes.  This     */
                    /* case is taken care of when decoder->subrs_len == 0.          */
                    zone->base = decoder->subrs[idx];

                    if (decoder->subrs_len)
                        zone->limit = zone->base + decoder->subrs_len[idx];
                    else
                    {
                        /* We are using subroutines from a CID font.  We must adjust */
                        /* for the seed bytes.                                       */
                        zone->base += (decoder->lenIV >= 0 ? decoder->lenIV : 0);
                        zone->limit = decoder->subrs[idx + 1];
                    }

                    zone->cursor = zone->base;

                    if (!zone->base)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " invoking empty subrs\n"));
                        goto Syntax_Error;
                    }

                    decoder->zone = zone;
                    ip = zone->base;
                    limit = zone->limit;
                    break;
                }

                case op_pop:
                    FT_TRACE4((" pop"));

                    if (known_othersubr_result_cnt > 0)
                    {
                        known_othersubr_result_cnt--;
                        /* ignore, we pushed the operands ourselves */
                        break;
                    }

                    if (unknown_othersubr_result_cnt == 0)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " no more operands for othersubr\n"));
                        goto Syntax_Error;
                    }

                    unknown_othersubr_result_cnt--;
                    top++; /* `push' the operand to callothersubr onto the stack */
                    break;

                case op_return:
                    FT_TRACE4((" return"));

                    if (zone <= decoder->zones)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " unexpected return\n"));
                        goto Syntax_Error;
                    }

                    zone--;
                    ip = zone->cursor;
                    limit = zone->limit;
                    decoder->zone = zone;
                    break;

                case op_dotsection:
                    FT_TRACE4((" dotsection"));

                    break;

                case op_hstem:
                    FT_TRACE4((" hstem"));

                    /* record horizontal hint */
                    if (hinter)
                    {
                        /* top[0] += builder->left_bearing.y; */
                        hinter->stem(hinter->hints, 1, top);
                    }
                    break;

                case op_hstem3:
                    FT_TRACE4((" hstem3"));

                    /* record horizontal counter-controlled hints */
                    if (hinter)
                        hinter->stem3(hinter->hints, 1, top);
                    break;

                case op_vstem:
                    FT_TRACE4((" vstem"));

                    /* record vertical hint */
                    if (hinter)
                    {
                        top[0] += orig_x;
                        hinter->stem(hinter->hints, 0, top);
                    }
                    break;

                case op_vstem3:
                    FT_TRACE4((" vstem3"));

                    /* record vertical counter-controlled hints */
                    if (hinter)
                    {
                        FT_Pos dx = orig_x;


                        top[0] += dx;
                        top[2] += dx;
                        top[4] += dx;
                        hinter->stem3(hinter->hints, 0, top);
                    }
                    break;

                case op_setcurrentpoint:
                    FT_TRACE4((" setcurrentpoint"));

                    /* From the T1 specification, section 6.4:                */
                    /*                                                        */
                    /*   The setcurrentpoint command is used only in          */
                    /*   conjunction with results from OtherSubrs procedures. */

                    /* known_othersubr_result_cnt != 0 is already handled     */
                    /* above.                                                 */

                    /* Note, however, that both Ghostscript and Adobe         */
                    /* Distiller handle this situation by silently ignoring   */
                    /* the inappropriate `setcurrentpoint' instruction.  So   */
                    /* we do the same.                                        */
                    #if 0

                    if (decoder->flex_state != 1)
                    {
                        FT_ERROR(("t1_decoder_parse_charstrings:"
                                  " unexpected `setcurrentpoint'\n"));
                        goto Syntax_Error;
                    }
                    else
                    #endif
                    decoder->flex_state = 0;
                    break;

                case op_unknown15:
                    FT_TRACE4((" opcode_15"));
                    /* nothing to do except to pop the two arguments */
                    break;

                default:
                    FT_ERROR(("t1_decoder_parse_charstrings:"
                              " unhandled opcode %d\n", op));
                    goto Syntax_Error;
            }

            /* XXX Operators usually clear the operand stack;  */
            /*     only div, callsubr, callothersubr, pop, and */
            /*     return are different.                       */
            /*     In practice it doesn't matter (?).          */

            decoder->top = top;

            #ifdef FT_DEBUG_LEVEL_TRACE
            FT_TRACE4(("\n"));
            bol = TRUE;
            #endif

        } /* general operator processing */

    } /* while ip < limit */

    FT_TRACE4(("..end..\n\n"));

Fail:
    return error;

Syntax_Error:
    return PSaux_Err_Syntax_Error;

Stack_Underflow:
    return PSaux_Err_Stack_Underflow;
}


/* parse a single Type 1 glyph */
FT_LOCAL_DEF(FT_Error)
t1_decoder_parse_glyph(T1_Decoder decoder,
                       FT_UInt glyph)
{
    return decoder->parse_callback(decoder, glyph);
}


/* initialize T1 decoder */
FT_LOCAL_DEF(FT_Error)
t1_decoder_init(T1_Decoder decoder,
                FT_Face face,
                FT_Size size,
                FT_GlyphSlot slot,
                FT_Byte * *glyph_names,
                PS_Blend blend,
                FT_Bool hinting,
                FT_Render_Mode hint_mode,
                T1_Decoder_Callback parse_callback)
{
    FT_MEM_ZERO(decoder, sizeof(*decoder));

    /* retrieve PSNames interface from list of current modules */
    {
        FT_Service_PsCMaps psnames = 0;


        FT_FACE_FIND_GLOBAL_SERVICE(face, psnames, POSTSCRIPT_CMAPS);
        if (!psnames)
        {
            FT_ERROR(("t1_decoder_init:"
                      " the `psnames' module is not available\n"));
            return PSaux_Err_Unimplemented_Feature;
        }

        decoder->psnames = psnames;
    }

    t1_builder_init(&decoder->builder, face, size, slot, hinting);

    /* decoder->buildchar and decoder->len_buildchar have to be  */
    /* initialized by the caller since we cannot know the length */
    /* of the BuildCharArray                                     */

    decoder->num_glyphs = (FT_UInt)face->num_glyphs;
    decoder->glyph_names = glyph_names;
    decoder->hint_mode = hint_mode;
    decoder->blend = blend;
    decoder->parse_callback = parse_callback;

    decoder->funcs = t1_decoder_funcs;

    return PSaux_Err_Ok;
}


/* finalize T1 decoder */
FT_LOCAL_DEF(void)
t1_decoder_done(T1_Decoder decoder)
{
    t1_builder_done(&decoder->builder);
}


/* END */